package uk.co.q3c.deplan.server.unit.dao;

import java.util.GregorianCalendar;

import org.apache.log4j.Logger;
import org.testng.Assert;
import org.testng.annotations.Test;

import uk.co.q3c.deplan.client.domain.dag.ChangeInvalidException;
import uk.co.q3c.deplan.client.domain.dag.CycleDetectedException;
import uk.co.q3c.deplan.client.domain.resource.IndividualResource;
import uk.co.q3c.deplan.client.domain.resource.PersistenceReferenceException;
import uk.co.q3c.deplan.client.domain.task.BaseTask;
import uk.co.q3c.deplan.server.unit.domain.task.DomainBase_UT;

@Test
public class TaskService_UT extends DomainBase_UT {
	transient protected final Logger logger = Logger.getLogger(getClass().getName());

	@Override
	protected void beforeMethod() throws Exception {
		super.beforeMethod();
		logger.info("<<<<<<<   test start >>>>>>>>");
	}

	@Override
	protected void afterMethod() throws Exception {
		super.afterMethod();
		logger.info(">>>>>>>>>  test end <<<<<<<<<");
	}

	/**
	 * 
	 */
	public void findById() {
		BaseTask pt1 = new BaseTask(connection);
		pt1.setName("first");
		pt1.save();
		BaseTask pt2 = new BaseTask(connection);
		pt2.save();
		BaseTask pt3 = new BaseTask(connection);
		pt3.save();
		Assert.assertEquals(testdao.count(BaseTask.class), 3);
		long taskId = pt3.getTaskId();
		connection.openSession();
		BaseTask result = (BaseTask) connection.taskService()
				.findById(taskId);
		Assert.assertNotNull(result);
		Assert.assertEquals("first", result.getName());

	}

	public void predecessors() throws PersistenceReferenceException {
		BaseTask at1 = new BaseTask(connection);
		BaseTask at2 = new BaseTask(connection);
		BaseTask at3 = new BaseTask(connection);
		at1.save();
		at2.save();
		at3.save();
		at1.addPredecessor(at2);
		Assert.assertEquals(at2.successors()
				.size(), 1);
		at1.addPredecessor(at3);
		Assert.assertEquals(at2.successors()
				.size(), 1);
		Assert.assertEquals(at1.predecessors()
				.size(), 2);

	}

	/**
	 * Object not saved before trying to add a predecessor
	 * 
	 * @throws PersistenceReferenceException
	 */
	@Test(expectedExceptions = { PersistenceReferenceException.class })
	public void addPredecessorBeforeSave() throws PersistenceReferenceException {
		BaseTask at1 = new BaseTask(connection);
		BaseTask at2 = new BaseTask(connection);
		at2.addPredecessor(at1);
	}

	/**
	 * Object not saved before trying to add a successor
	 * 
	 * @throws PersistenceReferenceException
	 */
	@Test(expectedExceptions = { PersistenceReferenceException.class })
	public void addSuccessorBeforeSave() throws PersistenceReferenceException {
		BaseTask at1 = new BaseTask(connection);
		BaseTask at2 = new BaseTask(connection);
		at2.addSuccessor(at1);
	}

	/**
	 * Predecessor itself not saved
	 * 
	 * @throws PersistenceReferenceException
	 */
	@Test(expectedExceptions = { PersistenceReferenceException.class })
	public void addPredecessorBeforeSave2() throws PersistenceReferenceException {
		BaseTask at1 = new BaseTask(connection);
		BaseTask at2 = new BaseTask(connection);
		at2.save();
		at2.addPredecessor(at1);
	}

	/**
	 * successor itself not saved
	 * 
	 * @throws PersistenceReferenceException
	 */
	@Test(expectedExceptions = { PersistenceReferenceException.class })
	public void addSuccessorBeforeSave2() throws PersistenceReferenceException {
		BaseTask at1 = new BaseTask(connection);
		BaseTask at2 = new BaseTask(connection);
		at2.save();
		at2.addSuccessor(at1);
	}

	public void rollup() throws CycleDetectedException, ChangeInvalidException, PersistenceReferenceException {
		BaseTask at1 = createStandardTask("at1");
		BaseTask at11 = createStandardTask("at11");
		BaseTask at12 = createStandardTask("at12");
		BaseTask at111 = createStandardTask("at111");
		BaseTask at112 = createStandardTask("at112");
		BaseTask at121 = createStandardTask("at121");
		BaseTask at122 = createStandardTask("at122");
		at11.setParentTask(at1);
		at12.setParentTask(at1);
		at111.setParentTask(at11);
		at112.setParentTask(at11);
		at121.setParentTask(at12);
		at122.setParentTask(at12);

		int c = connection.taskService()
				.childCount(at1);
		Assert.assertEquals(c, 2);

		// ============= work remaining =======================
		// work remaining set at leaf level
		at111.setWorkRemaining(111);
		at112.setWorkRemaining(112);
		at121.setWorkRemaining(121);
		at122.setWorkRemaining(122);

		connection.taskService()
				.rollup(at1);

		// first level summary
		Assert.assertEquals(at11.getWorkRemaining(), 111 + 112);
		Assert.assertEquals(at12.getWorkRemaining(), 121 + 122);

		// second level summary
		Assert.assertEquals(at1.getWorkRemaining(), 111 + 112 + 121 + 122);

		// ============= work done =======================
		IndividualResource resource = createResource("dsowerby");
		GregorianCalendar cal = new GregorianCalendar(2010, 4, 5);
		at111.setWorkDone(111, resource, cal.getTime());
		at112.setWorkDone(112, resource, cal.getTime());
		at121.setWorkDone(121, resource, cal.getTime());
		at122.setWorkDone(122, resource, cal.getTime());

		connection.taskService()
				.rollup(at1);

		// test total work as well, total work = work done + work remaining
		// first level summary
		Assert.assertEquals(at11.getWorkDone(), 111 + 112);
		Assert.assertEquals(at12.getWorkDone(), 121 + 122);
		Assert.assertEquals(at11.getTotalWork(), 111 + 112 + 111 + 112);
		Assert.assertEquals(at12.getTotalWork(), 121 + 122 + 121 + 122);

		// second level summary
		Assert.assertEquals(at1.getWorkDone(), 111 + 112 + 121 + 122);
		Assert.assertEquals(at1.getTotalWork(), 2 * (111 + 112 + 121 + 122));
		// ============= work allocated =======================
		// work allocated set at leaf level
		at111.setWorkAllocated(111);
		at112.setWorkAllocated(112);
		at121.setWorkAllocated(121);
		at122.setWorkAllocated(122);

		connection.taskService()
				.rollup(at1);

		// first level summary
		Assert.assertEquals(at11.getWorkAllocated(), 111 + 112);
		Assert.assertEquals(at12.getWorkAllocated(), 121 + 122);

		// second level summary
		Assert.assertEquals(at1.getWorkAllocated(), 111 + 112 + 121 + 122);

		// ============= start date =======================
		// actual start date set at leaf level
		at111.setStartRemaining(new GregorianCalendar(2009, 9, 5).getTime());
		at112.setStartRemaining(new GregorianCalendar(2009, 9, 1).getTime());
		at121.setStartRemaining(new GregorianCalendar(2009, 8, 7).getTime());
		at122.setStartRemaining(new GregorianCalendar(2009, 10, 11).getTime());
		connection.taskService()
				.rollup(at1);

		// first level summary, earliest start is at112 and at121
		Assert.assertEquals(at11.getStartRemaining(), at112.getStartRemaining());
		Assert.assertEquals(at12.getStartRemaining(), at121.getStartRemaining());

		// second level summary, earliest start is at121
		Assert.assertEquals(at1.getStartRemaining(), at121.getStartRemaining());

		// ============= plannedFinish date =======================
		// actual start date set at leaf level
		at111.setPlannedFinish(new GregorianCalendar(2009, 9, 6).getTime());
		at112.setPlannedFinish(new GregorianCalendar(2009, 9, 3).getTime());
		at121.setPlannedFinish(new GregorianCalendar(2009, 8, 6).getTime());
		at122.setPlannedFinish(new GregorianCalendar(2009, 10, 9).getTime());
		connection.taskService()
				.rollup(at1);

		// first level summary, latest finish is at111 and at122
		Assert.assertEquals(at11.getPlannedFinish(), at111.getPlannedFinish());
		Assert.assertEquals(at12.getPlannedFinish(), at122.getPlannedFinish());

		// second level summary, earliest start is at122
		Assert.assertEquals(at1.getPlannedFinish(), at122.getPlannedFinish());

		// ============= actual start date =======================
		// pushDownRollup has been checked with nulls in dates in tests above so it won't fall over
		// actual start date set at leaf level
		at111.setActualStart(new GregorianCalendar(2009, 9, 6).getTime());
		at112.setActualStart(new GregorianCalendar(2009, 9, 3).getTime());
		at121.setActualStart(new GregorianCalendar(2009, 8, 6).getTime());
		at122.setActualStart(new GregorianCalendar(2009, 10, 9).getTime());
		connection.taskService()
				.rollup(at1);

		// first level summary, earliest start is at112 and at121
		Assert.assertEquals(at11.getActualStart(), at112.getActualStart());
		Assert.assertEquals(at12.getActualStart(), at121.getActualStart());

		// second level summary, earliest start is at121
		Assert.assertEquals(at1.getActualStart(), at121.getActualStart());

		// ============= actual plannedFinish date =======================
		// actual plannedFinish date set at leaf level
		at111.setActualFinish(new GregorianCalendar(2009, 9, 12).getTime());
		at112.setActualFinish(new GregorianCalendar(2009, 9, 6).getTime());
		at121.setActualFinish(new GregorianCalendar(2009, 8, 7).getTime());
		at122.setActualFinish(new GregorianCalendar(2009, 10, 15).getTime());
		connection.taskService()
				.rollup(at1);

		// first level summary, latest finsih is at111 and at122
		Assert.assertEquals(at11.getActualFinish(), at111.getActualFinish());
		Assert.assertEquals(at12.getActualFinish(), at122.getActualFinish());

		// second level summary, earliest start is at122
		Assert.assertEquals(at1.getActualFinish(), at122.getActualFinish());

	}

	/**
	 * parent priority cascaded to sub tasks
	 * 
	 * @throws PersistenceReferenceException
	 * @throws ChangeInvalidException
	 * @throws CycleDetectedException
	 */
	public void pushDown() throws CycleDetectedException, ChangeInvalidException, PersistenceReferenceException {
		BaseTask at1 = createStandardTask("at1");
		BaseTask at11 = createStandardTask("at11");
		BaseTask at12 = createStandardTask("at12");
		BaseTask at111 = createStandardTask("at111");
		BaseTask at112 = createStandardTask("at112");
		BaseTask at121 = createStandardTask("at121");
		BaseTask at122 = createStandardTask("at122");
		at11.setParentTask(at1);
		at12.setParentTask(at1);
		at111.setParentTask(at11);
		at112.setParentTask(at11);
		at121.setParentTask(at12);
		at122.setParentTask(at12);
		// check deefaults
		at1.setUsingParentPriority(false);
		Assert.assertEquals(at1.getPriority(), 100);
		Assert.assertTrue(at11.isUsingParentPriority());
		connection.taskService()
				.pushDown(at1);
		at1.setPriority(333);
		connection.taskService()
				.pushDown(at1);
		Assert.assertEquals(at11.getPriority(), 333);
		Assert.assertEquals(at12.getPriority(), 333);
		Assert.assertEquals(at121.getPriority(), 333);
		Assert.assertEquals(at122.getPriority(), 333);
		at11.setUsingParentPriority(false);

		// change doesn't cascade automatically
		Assert.assertEquals(at11.getPriority(), 100);
		Assert.assertEquals(at111.getPriority(), 333);
		Assert.assertEquals(at112.getPriority(), 333);
		connection.taskService()
				.pushDown(at1);
		Assert.assertEquals(at111.getPriority(), 100);
		Assert.assertEquals(at112.getPriority(), 100);

	}

}
